home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr27 / cldda100.zip / CAL-DDA.PAS < prev    next >
Pascal/Delphi Source File  |  1995-02-01  |  15KB  |  576 lines

  1. PROGRAM Perpetual_Calendar;
  2. USES DOS, CRT;
  3.  
  4. CONST MinYear = 0001;       (* arbitrary limits; broad enough for  *)
  5.       MaxYear = 9999;       (*   for most practical cases purposes *)
  6.  
  7.       DaysPerYear = 365;
  8.       DaysPerWeek = 7;
  9.  
  10.       Margin = 1;
  11.       Between = 1;
  12.       StartRow = 4;
  13.       MaxDigits = 2;
  14.       Width = 2 * Margin + DaysPerWeek * MaxDigits +
  15.                           (DaysPerWeek-1) * Between + 2;
  16.  
  17.       IntenseFore = White;
  18.       Fore    = Black;       RevFore = LightGray;   BorderFore = White;
  19.       Back    = LightGray;   RevBack = Black;       BorderBack = LightGray;
  20.  
  21.  TYPE Month = (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec);
  22.  
  23.       Date = RECORD
  24.                da: 1..31;
  25.                mo: Month;
  26.                yr: MinYear..MaxYear
  27.              END;
  28.  
  29.       DayType = (Sun, Mon, Tue, Wed, Thu, Fri, Sat);
  30.  
  31.  {-----------------------}
  32.    {for the OpenWindow & CloseWindow procedures, which are by SALIM SAMAHA}
  33.  
  34.  Type
  35.    ScreenImage = Array [0..7999] of Word;  { enough for 132*60 }
  36.    FrameRec    = Record
  37.      Upperleft    : Word;
  38.      LowerRight   : Word;
  39.      ScreenMemory : ScreenImage;
  40.    end;
  41.  
  42.   VAR
  43.     SnapShot   : ^ScreenImage;
  44.     FrameStore : Array [1..10] of ^FrameRec;
  45.     WindowNum  : INTEGER;
  46.  {-----------------------}
  47.  
  48.   VAR maxDay: ARRAY [Month] OF INTEGER;
  49.       daysBefore: ARRAY [Month] OF INTEGER;
  50.  
  51.       savedDate: Date;
  52.       minDate, maxDate: Date;
  53.  
  54.    (* end of declarations *)
  55.  
  56. PROCEDURE Cursor(Const on : boolean);
  57.   (*= The Cursor procedure is not attributable =*)
  58. VAR
  59.   r : registers;
  60. BEGIN
  61.   r.ah:=$03;            {----get cursor shape on page 0               }
  62.   r.bh:=$00;            {----to be exact use function 2 to obtain page}
  63.   intr($10,r);
  64.  
  65.   if ((r.cx< $2000) and not(on)) or
  66.      ((r.cx>=$2000) and on)
  67.     then
  68.       begin
  69.         r.ah:=$01;
  70.         r.cx:=r.cx xor $2000;   {----toggle bit if neccesary}
  71.         intr($10,r);
  72.       end
  73. END; {of cursor}
  74.  
  75. FUNCTION IsLeapYear(Const yr: INTEGER): BOOLEAN;
  76. BEGIN
  77.   IsLeapYear := ((yr MOD 4 = 0) AND (yr MOD 100 <> 0)) OR (yr MOD 400 = 0)
  78. END;
  79.  
  80. FUNCTION NumDays(CONST d: Date): LONGINT;
  81.   (* NumDays returns an ordinal value for the date
  82.      with January 1, 0001 assigned the value 1.    *)
  83.   VAR result, leapYears, lYr: LONGINT;
  84. BEGIN
  85.   WITH d DO BEGIN
  86.     lYr:=yr-1;
  87.     result := (da);
  88.     INC(result, daysBefore[mo]);
  89.     INC(result,lYr * DaysPerYear);
  90.     leapYears := (lYr DIV 4) - (lYr DIV 100) + (lYr DIV 400);
  91.     INC(result, leapYears);
  92.     IF (mo > Feb) AND IsLeapYear(yr) THEN INC(result)
  93.   END;
  94.   NumDays := result
  95. END;
  96.  
  97. PROCEDURE MakeDate(Const n: LONGINT; VAR d: Date);
  98.   (* Takes an ordinal value compatible with that
  99.      returned by NumDays and forms the corresponding
  100.      date in d.                                      *)
  101.  
  102.   FUNCTION Before(Const mo: Month; Const yr: INTEGER): INTEGER;
  103.     (* This routine is the procedure equivalent of
  104.        the daysBefore array - except that it corrects
  105.        for leap years.                                *)
  106.     VAR i, max: Month;
  107.         result: INTEGER;
  108.   BEGIN
  109.     result := 0;
  110.     IF mo <> Jan THEN BEGIN
  111.       max := mo;
  112.       system.DEC(max);
  113.       FOR i := Jan TO max DO
  114.         INC(result, maxDay[i]);
  115.       IF (max > Jan) AND IsLeapYear(yr) THEN
  116.         INC(result)
  117.     END;
  118.     Before:=result
  119.   END;
  120.  
  121.   VAR c: INTEGER;
  122.       i: LONGINT;
  123. BEGIN
  124.   WITH d DO BEGIN
  125.     mo := Dec;
  126.     da := 31;
  127.     yr := n DIV DaysPerYear;
  128.     i := NumDays(d);
  129.     WHILE i >= n DO BEGIN
  130.       system.DEC(yr);
  131.       i := NumDays(d)
  132.     END;
  133.     INC(yr);
  134.     c := n - i;
  135.     WHILE (mo > Jan) AND (Before(mo, yr) >= c) DO
  136.       system.DEC(mo);
  137.     system.DEC(c, Before(mo, yr));
  138.     da := c
  139.   END
  140. END;
  141.  
  142. FUNCTION DayOfWeekF(Const d: Date): DayType;
  143. BEGIN
  144.   DayOfWeekF:= DayType(NumDays(d) MOD DaysPerWeek)
  145. END;
  146.  
  147. PROCEDURE WrMonth(Const mo: Month);
  148.   VAR s: string[3];
  149. BEGIN
  150.   CASE mo OF
  151.     Jan: s := 'Jan';
  152.     Feb: s := 'Feb';
  153.     Mar: s := 'Mar';
  154.     Apr: s := 'Apr';
  155.     May: s := 'May';
  156.     Jun: s := 'Jun';
  157.     Jul: s := 'Jul';
  158.     Aug: s := 'Aug';
  159.     Sep: s := 'Sep';
  160.     Oct: s := 'Oct';
  161.     Nov: s := 'Nov';
  162.     Dec: s := 'Dec';
  163.   END;
  164.   Write(s)
  165. END;
  166.  
  167. FUNCTION LastDay(Const mo: Month; Const yr: INTEGER): INTEGER;
  168.   VAR da: INTEGER;
  169. BEGIN
  170.   da := maxDay[mo];
  171.   IF (mo = Feb) AND IsLeapYear(yr) THEN INC(da);
  172.   LastDay := da
  173. END;
  174.  
  175. PROCEDURE DispDay(Const pos0: INTEGER; Const d: Date);
  176.   VAR x, y: INTEGER;
  177. BEGIN
  178.   x := Margin + ORD(DayOfWeekF(d)) * (MaxDigits+Between) + 1;
  179.   y := (d.da + pos0 - 1) DIV DaysPerWeek + StartRow;
  180.   GotoXY(x+1, y+1);
  181.   Write(d.da: MaxDigits)
  182. END;
  183.  
  184. PROCEDURE HiLite(Const pos0: INTEGER; Const d: Date);
  185. BEGIN
  186.   TextColor(RevFore);
  187.   TextBackGround(RevBack);
  188.     DispDay(pos0, d);
  189.   TextColor(Fore);
  190.   TextBackGround(Back)
  191. END;
  192.  
  193. PROCEDURE OpenWindow(Const UpLeftX, UpLeftY, LoRightX, LoRightY : INTEGER);
  194.   (*= The OpenWindow procedure is by SALIM SAMAHA, from SWAG =*)
  195. BEGIN
  196.   SnapShot := Ptr($B800, $0000);
  197.   Inc(WindowNum);
  198.   New(FrameStore[WindowNum]);
  199.   With Framestore[WindowNum]^ do
  200.   begin
  201.     ScreenMemory := SnapShot^;
  202.     UpperLeft    := WindMin;
  203.     LowerRight   := WindMax;
  204.   end;
  205.   Window(UpLeftX, UpLeftY, LoRightX, LoRightY);
  206. END;
  207.  
  208. PROCEDURE CloseWindow;
  209.   (*= The CloseWindow procedure is by SALIM SAMAHA, from SWAG =*)
  210. BEGIN
  211.   With Framestore[WindowNum]^ do
  212.   begin
  213.     Snapshot^ := ScreenMemory;
  214.     Window ((Lo(UpperLeft) + 1), (Hi(UpperLeft) + 1),
  215.             (Lo(LowerRight) + 1), (Hi(LowerRight) + 1));
  216.   end;
  217.   Dispose(Framestore[WindowNum]);
  218.   system.Dec(WindowNum);
  219. END;
  220.  
  221. PROCEDURE writechar (Const c : CHAR; Const attr, x, y : INTEGER); assembler;
  222.   (*= The writechar procedure is by John Giesbrect, from SWAG =*)
  223.  
  224. (*  assumes video page 0
  225.  *  upper left-hand corner is (1, 1)
  226.  *)
  227. ASM
  228.   mov ax, $0300   (* get cursor position *)
  229.   XOR bh, bh
  230.   INT $10
  231.   push dx         (* and save it *)
  232.   mov ax, $0200   (* set cursor position *)
  233.   XOR bh, bh
  234.   mov dh, BYTE PTR y
  235.   DEC dh
  236.   mov dl, BYTE PTR x
  237.   DEC dl
  238.   INT $10
  239.   mov ah, $09     (* write char and attribute *)
  240.   mov al, BYTE PTR c
  241.   XOR bh, bh
  242.   mov bl, BYTE PTR attr
  243.   mov cx, $0001
  244.   INT $10         (* restore original cursor position *)
  245.   mov ax, $0200
  246.   XOR bh, bh
  247.   pop dx
  248.   INT $10
  249. END;
  250.  
  251. PROCEDURE DisplayBorder(Const Left,Top,Rit,Bot,BoxSty : INTEGER);
  252.   (*= The DisplayBorder procedure is by DDA =*)
  253. Const
  254.   BoxChars=
  255.     #032#032#032#032#032#032+ {'      ' 0 - spaces (no boxes)            }
  256.     #218#196#191#179#192#217+ {'┌─┐│└┘' 1 - single-line characters       }
  257.     #213#205#184#179#212#190+ {'╒═╕│╘╛' 2 - single-line side, double top }
  258.     #214#196#183#186#211#189+ {'╓─╖║╙╜' 3 - double-line side, single top }
  259.     #201#205#187#186#200#188; {'╔═╗║╚╝' 4 - double-line characters       }
  260. VAR
  261.   ic : INTEGER;
  262.   Box : string[6];
  263. BEGIN
  264.   Box:=Copy(BoxChars,1+(6*BoxSty),6);
  265.     writechar(Box[1],TextAttr,Left,Top);
  266.   FOR ic := (Succ (Left)) to (Pred (Rit)) DO
  267.     writechar(Box[2],TextAttr,ic,Top);
  268.     writechar(Box[3],TextAttr,Rit,Top);
  269.   FOR ic := (Succ (Top)) to (Pred (Bot)) DO
  270.     writechar(Box[4],TextAttr,Left,ic);
  271.     writechar(Box[5],TextAttr,Left,Bot);
  272.   FOR ic := (Succ (Left)) to (Pred (Rit)) DO
  273.     writechar(Box[2],TextAttr,ic,Bot);
  274.   FOR ic := (Succ (Top)) to (Pred (Bot)) DO
  275.     writechar(Box[4],TextAttr,Rit,ic);
  276.     writechar(Box[6],TextAttr,Rit,Bot);
  277. END;
  278.  
  279. PROCEDURE Open_Window;
  280.   VAR Depth, Lft, Top, Rgt, Btm, BoxStyle : INTEGER;
  281. BEGIN
  282.   Depth := 6 + 2 + StartRow - 1;
  283.   Lft := ((1 + Lo(WindMax) - Width) DIV 2) + 1;
  284.   Top := ((1 + Hi(WindMax) - Depth) DIV 2) + 1;
  285.   Rgt := Lft + Width - 1;
  286.   Btm := Top + Depth - 1;
  287.   OpenWindow(Lft,Top,Rgt,Btm);
  288.   TextBackGround(Back);
  289.   ClrScr;
  290.   TextColor(BorderFore);
  291.   TextBackGround(BorderBack);
  292.   BoxStyle:=4;
  293.   DisplayBorder(Lft,Top,Rgt,Btm,BoxStyle);
  294.   Cursor(FALSE);
  295.   TextColor(Fore);
  296.   TextBackGround(Back);
  297.   GotoXY(((Width-8) DIV 2), Depth);
  298.   Write(' F1: help ')
  299. END;
  300.  
  301. PROCEDURE DispCalendar(d: Date; Const startPos: INTEGER);
  302.  
  303.   PROCEDURE WrHeading;
  304.     CONST MonthCol = 1+((Width-2) - 8) DIV 2 + 1;
  305.           DayLetter : String[7] = 'SMTWTFS';
  306.     VAR i: INTEGER;
  307.   BEGIN
  308.     GotoXY(MonthCol, 2);
  309.     WrMonth(d.mo);
  310.     Write(d.yr: 5);
  311.     WriteLn; WriteLn;
  312.  
  313.     TextColor(IntenseFore);
  314.     GotoXY(Margin+3,WhereY);
  315.     Write (DayLetter[1]);
  316.  
  317.     FOR i := 2 TO DaysPerWeek DO BEGIN
  318.       Write (' ':Between+1);
  319.       Write (DayLetter[i])
  320.     END;
  321.  
  322.     TextColor(Fore);
  323.     WriteLn
  324.   END;
  325.  
  326.   VAR i, max: INTEGER;
  327.       x1,y1,x2,y2 : INTEGER;
  328. BEGIN
  329.   x1:=1+Lo(WindMin); y1:=1+Hi(WindMin);
  330.   x2:=1+Lo(WindMax); y2:=1+Hi(WindMax);
  331.   Window(x1+1,y1+1,x2-1,y2-1); ClrScr; Window(x1,y1,x2,y2);
  332.  
  333.   WrHeading;
  334.   max := LastDay(d.mo, d.yr);
  335.   FOR i := 1 TO max DO BEGIN
  336.     d.da := i;
  337.     DispDay(startPos, d)
  338.   END
  339. END;
  340.  
  341. PROCEDURE IncDate(VAR d: Date; Const n: LONGINT);
  342.   (* Increments the date by the value n. *)
  343.   VAR i: LONGINT;
  344. BEGIN
  345.   WITH d DO BEGIN
  346.     i := NumDays(d);
  347.     INC(i, n);
  348.     MakeDate(i, d)
  349.   END
  350. END;
  351.  
  352. PROCEDURE DecDate(VAR d: Date; Const n: LONGINT);
  353.   (* Decrements the date by the value n. *)
  354.   VAR i: LONGINT;
  355. BEGIN
  356.   WITH d DO BEGIN
  357.     i := NumDays(d);
  358.     system.DEC(i, n);
  359.     MakeDate(i, d)
  360.   END
  361. END;
  362.  
  363. PROCEDURE ShowHelp; Forward;
  364.  
  365. PROCEDURE HandleScanCode(Const pos0: INTEGER; VAR d: Date; VAR refresh: BOOLEAN);
  366.   CONST (* scan codes *)
  367.         home  = #71; up    = #72; pgUp  = #73;
  368.         left  = #75;              right = #77;
  369.                      down  = #80; pgDn  = #81;
  370.         F1 = #59;
  371.         ctrlPgUp = #132;
  372.         ctrlPgDn = #118;
  373.  
  374.   VAR sc: CHAR; (* scan code *)
  375.       d0: Date; (* date on entry *)
  376.       max: INTEGER;
  377. BEGIN
  378.   d0 := d;
  379.   sc := ReadKey;
  380.   CASE sc OF
  381.     F1:
  382.       ShowHelp;
  383.     left:
  384.       IF NumDays(d) > NumDays(minDate) THEN BEGIN
  385.         DispDay(pos0, d);
  386.         DecDate(d, 1);
  387.         HiLite(pos0, d)
  388.       END;
  389.     right:
  390.       IF NumDays(d) < NumDays(maxDate) THEN BEGIN
  391.         DispDay(pos0, d);
  392.         IncDate(d, 1);
  393.         HiLite(pos0, d)
  394.       END;
  395.     up:
  396.       IF NumDays(d) >= (NumDays(minDate) + DaysPerWeek) THEN BEGIN
  397.         DispDay(pos0, d);
  398.         DecDate(d, DaysPerWeek);
  399.         HiLite(pos0, d)
  400.       END;
  401.     down:
  402.       IF (NumDays(d) + DaysPerWeek) <= NumDays(maxDate) THEN BEGIN
  403.         DispDay(pos0, d);
  404.         IncDate(d, DaysPerWeek);
  405.         HiLite(pos0, d)
  406.       END;
  407.     pgUp:
  408.       BEGIN
  409.         IF d.mo > Jan THEN system.DEC(d.mo)
  410.         ELSE BEGIN
  411.           IF d.yr > MinYear THEN BEGIN
  412.             system.DEC(d.yr);
  413.             d.mo := Dec
  414.           END;
  415.         END;
  416.         max := LastDay(d.mo, d.yr);
  417.         IF d.da > max THEN d.da := max;
  418.       END;
  419.     pgDn:
  420.       BEGIN
  421.         IF d.mo < Dec THEN INC(d.mo)
  422.         ELSE BEGIN
  423.           IF d.yr < MaxYear THEN BEGIN
  424.             INC(d.yr);
  425.             d.mo := Jan
  426.           END
  427.         END;
  428.         max := LastDay(d.mo, d.yr);
  429.         IF d.da > max THEN d.da := max;
  430.       END;
  431.     ctrlPgUp:
  432.       IF d.yr > MinYear THEN BEGIN
  433.         system.DEC(d.yr);
  434.         IF (d.mo = Feb) AND (d.da = 29) THEN
  435.           d.da := LastDay(d.mo, d.yr);
  436.       END;
  437.     ctrlPgDn:
  438.       IF d.yr < MaxYear THEN BEGIN
  439.         INC(d.yr);
  440.         IF (d.mo = Feb) AND (d.da = 29) THEN
  441.           d.da := LastDay(d.mo, d.yr)
  442.       END;
  443.     home:
  444.       BEGIN
  445.         DispDay(pos0, d);
  446.         d := savedDate;
  447.         HiLite(pos0, d)
  448.       END;
  449.   END;
  450.   refresh := (d.mo <> d0.mo) OR (d.yr <> d0.yr)
  451. END;
  452.  
  453. PROCEDURE GetSelDate(VAR d: Date);
  454. (* General routine that allows the user to select
  455.    a date by positioning a "cursor" on the desired
  456.    date and pressing return; if <Esc> is pressed,
  457.    the date is left unchanged and abort becomes TRUE.
  458.  
  459.    d should be seeded with a valid date, which will
  460.    determine the starting date upon calling the
  461.    procedure.                                         *)
  462.  
  463.   CONST nul = #0;
  464.         cr  = #13;
  465.         esc = #27;
  466.   VAR ch: CHAR;
  467.       refresh: BOOLEAN;      (* rebuild display *)
  468.       startPos: INTEGER;    (* horizontal offset *)
  469.       savedDay: INTEGER;
  470. BEGIN
  471.   savedDate := d;
  472.   Open_Window;
  473.   refresh := TRUE;
  474.   REPEAT
  475.     IF refresh THEN BEGIN
  476.       savedDay := d.da;
  477.       d.da := 1;
  478.       startPos := ORD(DayOfWeekF(d));
  479.       d.da := savedDay;
  480.       DispCalendar(d, startPos);
  481.       HiLite(startPos, d)
  482.     END;
  483.     ch := ReadKey;
  484.     IF ch = nul THEN HandleScanCode(startPos, d, refresh)
  485.   UNTIL (ch = esc) OR (ch = cr);
  486.  
  487.   IF (ch = esc) THEN d := savedDate;
  488.   CloseWindow
  489. END;
  490.  
  491. PROCEDURE InitMax;
  492. BEGIN
  493.   WITH minDate DO BEGIN
  494.     mo := Jan;
  495.     da := 1;
  496.     yr := MinYear
  497.   END;
  498.   WITH maxDate DO BEGIN
  499.     mo := Dec;
  500.     da := 31;
  501.     yr := MaxYear
  502.   END
  503. END;
  504.  
  505. PROCEDURE MonthsInit;
  506.   VAR mo: Month;
  507. BEGIN
  508.   maxDay[Jan] := 31;
  509.   maxDay[Feb] := 28;  (* adjust for leap years later *)
  510.   maxDay[Mar] := 31;
  511.   maxDay[Apr] := 30;
  512.   maxDay[May] := 31;
  513.   maxDay[Jun] := 30;
  514.   maxDay[Jul] := 31;
  515.   maxDay[Aug] := 31;
  516.   maxDay[Sep] := 30;
  517.   maxDay[Oct] := 31;
  518.   maxDay[Nov] := 30;
  519.   maxDay[Dec] := 31;
  520.  
  521.   daysBefore[Jan] := 0;
  522.   FOR mo := Jan TO Nov DO
  523.     daysBefore[Month(ORD(mo)+1)] := daysBefore[mo] + maxDay[mo]
  524. END;
  525.  
  526. PROCEDURE GetSysDate(VAR d: Date);
  527.   (* Reads the system clock and assigns the date to d
  528.      and the day of the week to dayOfWeek.            *)
  529.   VAR SysYear,SysMonth,SysDay,SysDOW : word;
  530. BEGIN
  531.   GetDate(SysYear,SysMonth,SysDay,SysDOW);
  532.   d.yr := SysYear;
  533.   d.mo := Month(SysMonth-1);
  534.   d.da := SysDay
  535. { dayOfWeek := DayType(SysDOW+1);   }
  536. END;
  537.  
  538. PROCEDURE Wrl(Const s : String);
  539. BEGIN
  540.   WriteLn(s);
  541. END;
  542.  
  543. PROCEDURE ShowHelp;
  544.   VAR tkey : char;
  545. BEGIN
  546.   OpenWindow(2+Lo(WindMin),2+Hi(WindMin),Lo(WindMax),Hi(WindMax));
  547.   ClrScr;
  548.   GotoXY(1,1);
  549.     Wrl('Free calendar (by DDA)');
  550.  
  551.     Wrl('Date:  '#27#32#26);
  552.     Wrl('Week:  '#24#32#25);
  553.     Wrl('Month: PgUp/ PgDn');
  554.     Wrl('Year:  Ctrl-PgUp/ PgDn');
  555.  
  556.     Wrl('Current date: Home');
  557.     Write('Exit:  Escape');
  558.   tkey:=ReadKey;
  559.   if tkey=#0 then ReadKey;
  560.   CloseWindow;
  561. END;
  562.  
  563.   VAR d: Date;
  564.       x, y : integer;
  565.  
  566. BEGIN
  567.   x:=WhereX; y:=WhereY;
  568.   MonthsInit;
  569.   InitMax;
  570.   WindowNum:=1;
  571.   GetSysDate(d);
  572.   GetSelDate(d);
  573.   GotoXY (x,y);
  574.   Cursor(TRUE);
  575. END.
  576.